package org.example;
import java.nio.charset.StandardCharsets;
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.text.DateFormat;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.List;
import java.util.logging.*;

public class BambooHoleDetection {
    // 日志记录器
    private static final Logger logger = Logger.getLogger(BambooHoleDetection.class.getName());
    // 历史记录文件路径
    private static final String HISTORY_FILE_PATH = "bamboo_hole_detection_history.txt";

    // 虫洞计数器改为类成员变量
    private static int holeCount = 0;

    static {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        initLogger();
    }

    private static void initLogger() {
        try {
            FileHandler fileHandler = new FileHandler("bamboo_hole_detection.log", 1024 * 1024, 5, true);
            fileHandler.setFormatter(new SimpleFormatter());
            logger.addHandler(fileHandler);
            logger.setLevel(Level.INFO);

            Logger rootLogger = Logger.getLogger("");
            Handler[] handlers = rootLogger.getHandlers();
            if (handlers.length > 0 && handlers[0] instanceof ConsoleHandler) {
                rootLogger.removeHandler(handlers[0]);
            }
        } catch (IOException e) {
            logger.log(Level.SEVERE, "日志初始化失败", e);
        }
    }

    private static final Map<String, ResourceBundle> LANGUAGE_BUNDLES = new HashMap<>();
    private static ResourceBundle currentBundle;
    private static JComboBox<String> languageSelector;
    private static JLabel holeCountLabel;

    private static Mat src;
    private static Mat sobelImage;
    private static Mat sobelBinary;
    private static Mat detectedImage;

    private static final List<File> IMAGE_FILES = new ArrayList<>();
    private static int currentIndex = 0;
    private static final JLabel originalImageLabel = new JLabel();
    private static final JLabel sobelImageLabel = new JLabel();
    private static final JLabel sobelBinaryLabel = new JLabel();
    private static final JLabel detectedImageLabel = new JLabel();

    static {
        String baseName = "messages";
        Map<Locale, String> localeDisplayNames = new HashMap<>();
        localeDisplayNames.put(Locale.SIMPLIFIED_CHINESE, "简体中文");

        for (Map.Entry<Locale, String> entry : localeDisplayNames.entrySet()) {
            Locale locale = entry.getKey();
            String displayName = entry.getValue();
            try {
                Properties props = new Properties();
                String filePath = baseName + "_" + locale.toString() + ".properties";
                try (InputStream is = BambooHoleDetection.class.getClassLoader().getResourceAsStream(filePath)) {
                    if (is == null) {
                        throw new MissingResourceException("资源文件缺失", "BambooHoleDetection", filePath);
                    }
                    props.load(new InputStreamReader(is, StandardCharsets.UTF_8));
                }
                ResourceBundle bundle = new ResourceBundle() {
                    @Override
                    protected Object handleGetObject(String key) {
                        return props.getProperty(key);
                    }

                    @Override
                    public Enumeration<String> getKeys() {
                        return Collections.enumeration(props.stringPropertyNames());
                    }
                };
                LANGUAGE_BUNDLES.put(displayName, bundle);
            } catch (IOException | MissingResourceException e) {
                logger.log(Level.SEVERE, "资源文件加载失败: " + locale, e);
            }
        }
        currentBundle = LANGUAGE_BUNDLES.get("简体中文");
        if (currentBundle == null) {
            logger.severe("未找到默认语言包");
        }
    }

    public static void main(String[] args) {
        logger.info("===== 竹片虫洞检测系统启动 =====");
        recordRunHistory();

        SwingUtilities.invokeLater(() -> {
            try {
                File selectedFolder = openFolderChooser();
                if (selectedFolder == null) {
                    logger.warning("用户取消选择文件夹");
                    return;
                }

                loadImagesFromFolder(selectedFolder);
                if (IMAGE_FILES.isEmpty()) {
                    logger.warning("所选文件夹无有效图像");
                    return;
                }

                createMainWindow();
            } catch (Exception e) {
                logger.log(Level.SEVERE, "程序运行出错", e);
            }
        });
    }

    private static void recordRunHistory() {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(HISTORY_FILE_PATH, true))) {
            DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            writer.write("运行时间: " + dateFormat.format(new Date()));
            writer.newLine();
            writer.write("-----------------------------------");
            writer.newLine();
        } catch (IOException e) {
            logger.log(Level.SEVERE, "记录历史失败", e);
        }
    }

    private static void loadImagesFromFolder(File folder) {
        File[] files = folder.listFiles(file -> {
            String name = file.getName().toLowerCase();
            return name.endsWith(".jpg") || name.endsWith(".png") || name.endsWith(".jpeg");
        });

        if (files != null) {
            IMAGE_FILES.clear();
            Collections.addAll(IMAGE_FILES, files);
            logger.info("加载 " + IMAGE_FILES.size() + " 张图像");
        } else {
            logger.warning("无法读取文件夹: " + folder.getAbsolutePath());
        }
    }

    private static JFrame frame;
    private static JScrollPane fileListScrollPane;

    private static void createMainWindow() {
        frame = new JFrame(currentBundle != null ? currentBundle.getString("title") : "竹片虫洞检测系统");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(1200, 850);

        // 语言选择器
        languageSelector = new JComboBox<>(LANGUAGE_BUNDLES.keySet().toArray(new String[0]));
        languageSelector.addActionListener(e -> updateLanguage());

        JLabel languageLabel = new JLabel(currentBundle != null ? currentBundle.getString("language") : "语言");
        JPanel topPanel = new JPanel();
        topPanel.add(languageLabel);
        topPanel.add(languageSelector);

        // 左侧文件列表
        JList<File> fileList = new JList<>(IMAGE_FILES.toArray(new File[0]));
        fileList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        fileList.addListSelectionListener(e -> {
            currentIndex = fileList.getSelectedIndex();
            if (currentIndex >= 0) {
                logger.info("选择图像: " + IMAGE_FILES.get(currentIndex).getName());
                processCurrentImage();
            }
        });

        fileListScrollPane = new JScrollPane(fileList);
        fileListScrollPane.setPreferredSize(new Dimension(200, 0));
        fileListScrollPane.setBorder(BorderFactory.createTitledBorder(
                currentBundle != null ? currentBundle.getString("image_list") : "图像列表"));

        // 右侧图像显示
        JPanel imagePanel = new JPanel(new GridLayout(3, 2));
        imagePanel.add(wrapInScroll(originalImageLabel, "original_image"));
        imagePanel.add(wrapInScroll(sobelImageLabel, "sobel_image"));
        imagePanel.add(wrapInScroll(sobelBinaryLabel, "sobel_binary"));
        imagePanel.add(wrapInScroll(detectedImageLabel, "detection_result"));

        // 虫洞数量标签
        holeCountLabel = new JLabel("检测到 0 个虫洞");
        JPanel countPanel = new JPanel();
        countPanel.setBorder(BorderFactory.createTitledBorder(
                currentBundle != null ? currentBundle.getString("detection_count") : "检测结果"));
        countPanel.add(holeCountLabel);
        imagePanel.add(countPanel);
        imagePanel.add(new JLabel()); // 占位

        JPanel mainPanel = new JPanel(new BorderLayout());
        mainPanel.add(fileListScrollPane, BorderLayout.WEST);
        mainPanel.add(imagePanel, BorderLayout.CENTER);

        frame.add(topPanel, BorderLayout.NORTH);
        frame.add(mainPanel, BorderLayout.CENTER);
        frame.setVisible(true);

        // 加载首张图像
        if (!IMAGE_FILES.isEmpty()) {
            fileList.setSelectedIndex(0);
            logger.info("自动加载首张图像: " + IMAGE_FILES.get(0).getName());
        }
    }

    private static void updateLabelsAndTitles() {
        if (frame != null && currentBundle != null) {
            frame.setTitle(currentBundle.getString("title"));
        }

        if (fileListScrollPane != null && currentBundle != null) {
            fileListScrollPane.setBorder(BorderFactory.createTitledBorder(currentBundle.getString("image_list")));
        }

        for (JComponent component : Arrays.asList(originalImageLabel, sobelImageLabel, sobelBinaryLabel, detectedImageLabel)) {
            JScrollPane parentScrollPane = (JScrollPane) component.getParent().getParent();
            String key = parentScrollPane.getName();
            if (key != null && currentBundle != null) {
                parentScrollPane.setBorder(BorderFactory.createTitledBorder(currentBundle.getString(key)));
            }
        }

        if (languageSelector != null && currentBundle != null) {
            ((JLabel) languageSelector.getParent().getComponent(0))
                    .setText(currentBundle.getString("language"));
        }

        if (holeCountLabel != null && currentBundle != null) {
            ((JPanel) holeCountLabel.getParent()).setBorder(BorderFactory.createTitledBorder(
                    currentBundle.getString("detection_count")));
        }
    }

    private static void updateLanguage() {
        String selectedLanguage = (String) languageSelector.getSelectedItem();
        if (selectedLanguage != null) {
            currentBundle = LANGUAGE_BUNDLES.get(selectedLanguage);
            logger.info("语言切换为: " + selectedLanguage);
            SwingUtilities.invokeLater(BambooHoleDetection::updateLabelsAndTitles);
            if (!IMAGE_FILES.isEmpty()) {
                processCurrentImage();
            }
        }
    }

    private static void processCurrentImage() {
        File file = IMAGE_FILES.get(currentIndex);
        src = Imgcodecs.imread(file.getAbsolutePath());

        if (src.empty()) {
            logger.warning("图像加载失败: " + file.getName());
            return;
        }

        logger.info("加载图像: " + file.getName() + "，尺寸: " + src.width() + "x" + src.height());

        // 1. 转灰度
        Mat gray = new Mat();
        Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);

        // 2. 高斯模糊（核大小5x5）
        Mat blurred = new Mat();
        Imgproc.GaussianBlur(gray, blurred, new Size(5, 5), 0);

        // 3. 自适应阈值高斯加权、二值化反转）
        Mat thresholdMat = new Mat();
        Imgproc.adaptiveThreshold(blurred, thresholdMat, 255,
                Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C, Imgproc.THRESH_BINARY_INV, 11, 2);

        // 4. 形态学开运算+闭运算 核大小5x5
        Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(5, 5));
        Mat morph = new Mat();
        Imgproc.morphologyEx(thresholdMat, morph, Imgproc.MORPH_OPEN, kernel);   // 去噪
        Imgproc.morphologyEx(morph, morph, Imgproc.MORPH_CLOSE, kernel);  // 填充空洞


        sobelImage = blurred;  // 显示模糊后的灰度图
        sobelBinary = morph;   // 显示二值化+形态学后的结果

        detectedImage = src.clone();
        updateDetection();

        // 更新界面显示
        originalImageLabel.setIcon(new ImageIcon(matToImage(src)));
        sobelImageLabel.setIcon(new ImageIcon(matToImage(sobelImage)));
        sobelBinaryLabel.setIcon(new ImageIcon(matToImage(sobelBinary)));
        detectedImageLabel.setIcon(new ImageIcon(matToImage(detectedImage)));

        logger.info("图像处理完成: " + file.getName());
    }


    private static void updateDetection() {
        if (src.empty()) return;

        detectedImage = src.clone();
        List<MatOfPoint> contours = new ArrayList<>();
        Mat hierarchy = new Mat();


        Imgproc.findContours(sobelBinary, contours, hierarchy,
                Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);

        holeCount = 0; // 重置计数器
        for (MatOfPoint contour : contours) {
            double area = Imgproc.contourArea(contour);


            if (area > 100 && area < 7000) {
                Rect bbox = Imgproc.boundingRect(contour);

                // 计算圆形度
                MatOfPoint2f contour2f = new MatOfPoint2f(contour.toArray());
                double perimeter = Imgproc.arcLength(contour2f, true);
                double circularity = 4 * Math.PI * area / (perimeter * perimeter);

                // 圆形度过滤
                if (circularity > 0.3) {
                    Imgproc.rectangle(detectedImage, bbox.tl(), bbox.br(),
                            new Scalar(0, 255, 0), 2);
                    holeCount++;
                }
                contour2f.release();
            }
            contour.release();
        }

        // 更新UI
        SwingUtilities.invokeLater(() -> {
            if (holeCountLabel != null && currentBundle != null) {
                holeCountLabel.setText(MessageFormat.format(
                        currentBundle.getString("detection_count"), holeCount));
            }
        });

        logger.info("检测到 " + holeCount + " 个虫洞: " + IMAGE_FILES.get(currentIndex).getName());
        appendDetectionResult(IMAGE_FILES.get(currentIndex).getName(), holeCount);
        hierarchy.release();
    }

    private static void appendDetectionResult(String imageName, int holeCount) {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(HISTORY_FILE_PATH, true))) {
            writer.write("图像: " + imageName + " - 检测到 " + holeCount + " 个虫洞");
            writer.newLine();
        } catch (IOException e) {
            logger.log(Level.SEVERE, "追加结果失败", e);
        }
    }

    private static Image matToImage(Mat mat) {
        MatOfByte buffer = new MatOfByte();
        try {
            Imgcodecs.imencode(".jpg", mat, buffer);
            return Toolkit.getDefaultToolkit().createImage(buffer.toArray());
        } finally {
            buffer.release();
        }
    }

    private static File openFolderChooser() {
        JFileChooser chooser = new JFileChooser();
        chooser.setDialogTitle(currentBundle != null ?
                currentBundle.getString("title") : "选择图像文件夹");
        chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        int result = chooser.showOpenDialog(null);
        return result == JFileChooser.APPROVE_OPTION ? chooser.getSelectedFile() : null;
    }

    private static JScrollPane wrapInScroll(JComponent component, String key) {
        JScrollPane scroll = new JScrollPane(component);
        scroll.setName(key);
        scroll.setBorder(BorderFactory.createTitledBorder(
                currentBundle != null ? currentBundle.getString(key) : key));
        return scroll;
    }
}